/* iBeacon Locator class.
* Neil Z. Shao
*/
#pragma once
#include <vector>
#include <queue>
#include <string>

namespace easyshow
{
	struct BeaconInfo
	{
		std::string beaconAddress;
		float pos_x, pos_y;
		int map_id, beacon_id;
		float distance;
	};

	struct BeaconMap
	{
		int width, height;
		int map_id;
		std::string map_name;
		std::vector<float> valid_pos;
	};

	struct BeaconData
	{
		std::string beaconAddress;
		float distance;
	};

	struct UserPos
	{
		float pos_x = -1, pos_y = -1;
		float conf = 0;
		int map_id = -1;
		float dir_x = 0, dir_y = 0;
	};

	/* BeaconLocator. 
	* Use addBeaconInfo() to setup beacon locations and addBeaconMap() to setup map.
	* Call predictPosition() with received beacon signals to predict current position.
	*/
	class BeaconLocator
	{
	public:
		BeaconLocator();
		~BeaconLocator();

		// setup
		void setMedianFilterWindow(int n_median) { n_median_ = n_median; }
		void addBeaconInfo(BeaconInfo beacon_info);
		void addBeaconMap(BeaconMap beacon_map);
		bool init(std::string config_fn, bool reset);

		// predict
		UserPos predictPosition(std::vector<BeaconData> beacon_datas);
		UserPos predictPosition(std::string beacon_data_str);
		void predictPositionParticleFilter(UserPos &user_pos);

		// clear
		void clearAll();
		void clearReceivedData();

		// helper functions
		std::vector<BeaconInfo> beacon_infos() { return beacon_infos_; }
		std::vector<BeaconMap> beacon_maps() { return beacon_maps_; }
		BeaconMap &beacon_map(int map_id) { return beacon_maps_[findMapIdx(map_id)]; }

	private:
		int findBeaconIdx(std::string beaconAddress);
		int findMapIdx(int map_id);

		int n_beacons_ = 0, n_records_ = 0;
		int n_median_ = 2;
		std::vector<BeaconInfo> beacon_infos_;
		std::vector<BeaconMap> beacon_maps_;
		std::vector<std::deque<float> > distances_;
		UserPos user_pos_, last_pos_;
		std::deque<float> pos_xs_, pos_ys_;
	};
}
